classdef dotsComputable < dotsAllManagedObjects
    % @class dotsComputable
    % Superclass for objects that do computations with data.
    %
    % @ingroup computable
    
    properties (SetObservable = true)
        % how often to invoke compute() on this object, automatically
        % @details
        % dotsTheComputablesManager may periodically invoke compute() on
        % objects in its active group.  Each object may use its own
        % recomputeInterval to specify how often this is necessary.
        % @details
        % Only finite, non-negative values recomputeInterval will be used,
        % otherwise compute() will not be invoked automatically.
        % recomputeInterval should be in the same units as the
        % clockFunction of dotsTheComputablesManager.
        recomputeInterval = nan;
        
        % the result of the most recent compute()
        % @details
        % dotsComputeable objects should always store the result of
        % the most recent compute() in computeResult.
        computeResult;
    end
    
    properties (SetObservable = false)
        % when next to invoke compute() on this object, automatically
        % @details
        % dotsTheComputablesManager may periodically invoke compute() on
        % objects in its active group.  Each object uses recomputeTime to
        % keep track of when next it must compute().
        % @details
        % recomputeTime should be in the same units as the clockFunction of
        % dotsTheComputablesManager.
        recomputeTime = nan;
    end
    
    methods
        % Constructor takes no arguments.
        function self = dotsComputable
            self = self@dotsAllManagedObjects;
        end
        
        % do any pre-precomputation setup
        % @details
        % Subclasses should extend prepareToCompute() to do any one-time
        % preparations for compute() calls that may follow, for example
        % building a large table or initializing a data connection.
        % The superclass implementation of prepareToCompute() resets
        % recomputeTime so that subsequent calls to mayComputeNow() may
        % cause periodic re-compute() behavior.
        function prepareToCompute(self)
            if isfinite(self.recomputeInterval) ...
                    && self.recomputeInterval >= 0
                self.recomputeTime = -inf;
            end
        end
        
        % compute() or not, depending the current time
        function [result, didCompute] = mayComputeNow(self, currentTime)
            if currentTime >= self.recomputeTime
                self.recomputeTime = currentTime + self.recomputeInterval;
                result = self.compute;
                didCompute = true;
            else
                result = [];
                didCompute = false;
            end
        end
        
        % subclass must redefine compute()
        % @details
        % compute() must return at least one output, the first of which
        % should also be stored in computeResult.
        % @details
        % To facilitate automatic re-compute() behaviors, compute() should
        % not be expected to take any arguments.
        function result = compute(self)
            result = [];
            self.computeResult = result;
        end
    end
end